package article

import (
	"encoding/json"
	"fmt"
	"html/template"
	"math"
	"myblogs/Lib"
	"myblogs/Lib/redis"
	"myblogs/app/http/model/category"
	"myblogs/app/variable"
	"strconv"
	"time"
)

type ArticleModel struct {
	ArticleId       string
	Id              int64
	Title           string
	Summary         string
	Content         template.HTML
	CategoryId      int64  `db:"category_id"`
	CategoryName    string `db:"category_name"`
	Click           int64
	LikeCount       int64     `db:"like_count"`
	CommentCount    int64     `db:"comment_count"`
	CreatedBy       string    `db:"created_by"`
	CreatedTime     time.Time `db:"created_time"`
	CreatedTimeStr  string
	CreatedTimeStr2 string
	Cover           string
	Tags            string
	Sticky          int8
}

var (
	categoryModel *category.CategoryModel
)

func init() {
	categoryModel = &category.CategoryModel{}
}

// GetArticleLatest 获取最新文章
func (a *ArticleModel) GetArticleLatest() []*ArticleModel {
	var articleModels []*ArticleModel

	articleLatest, err := redis.RedisClient().Get("Article:Latest").Result()
	if err != nil {
		fmt.Printf("redis error:%s\n", err)
	} else {
		if articleLatest != "" {
			err = json.Unmarshal([]byte(articleLatest), &articleModels)
			if err != nil {
				fmt.Printf("error: %s\n", err)
			} else {
				return articleModels
			}
		}
	}

	strSql := "select id, title, summary, content, created_by, created_time, click, like_count, comment_count, category_name, cover, category_id, sticky " +
		"from article " +
		"where status=1 " +
		"order by like_count desc " +
		"limit 5"
	err = variable.Db.Select(&articleModels, strSql)
	if err != nil {
		fmt.Printf("%s", err)
		return nil
	}
	for _, article := range articleModels {
		article.ArticleId, _ = Lib.EnPwdCode([]byte(strconv.FormatInt(article.Id, 10)))
	}
	jsonData, _ := json.Marshal(articleModels)
	_, err = redis.RedisClient().Set("Article:Latest", jsonData, time.Hour*24).Result()
	if err != nil {
		fmt.Printf("%v\n", err)
	}
	return articleModels
}

// GetArticleTotal 获取文章总数
func (a *ArticleModel) GetArticleTotal() int64 {
	strSql := "select count(*) from article where status=1"
	var total int64
	err := variable.Db.Get(&total, strSql)
	if err != nil {
		fmt.Printf("%s", err)
		return 0
	}
	return total
}

// GetArticlePage 文章分页列表
func (a *ArticleModel) GetArticlePage(page int64, pageSize int64, keyword string, tag string, category string, sort string) (pageResult map[string]interface{}) {
	where := ""
	if keyword != "" {
		where = "AND (title LIKE '%" + keyword + "%' OR content LIKE '%" + keyword + "%' OR tags LIKE '%" + keyword + "%')"
	}

	if tag != "" {
		where = "AND tags LIKE '%" + tag + "%'"
	}

	if category != "" {
		where = "AND category_name LIKE '%" + category + "%'"
	}

	order := " ORDER BY id DESC "
	switch sort {
	case "1": // 最新文章
		order = " ORDER BY IF(sticky=1, 2, 1) DESC, id DESC "
	case "2": // 热门文章
		order = " ORDER BY IF(sticky=1, 2, 1) DESC, click DESC "
	case "3": // 最近更新
		order = " ORDER BY IF(sticky=1, 2, 1) DESC, updated_time DESC "
	case "4": // 最多点赞
		order = " ORDER BY IF(sticky=1, 2, 1) DESC, like_count DESC "
	}

	// 总记录数
	var total int64
	var articles []*ArticleModel
	strSql := "SELECT COUNT(*) FROM article WHERE status=1 " + where
	err := variable.Db.Get(&total, strSql)
	if err != nil {
		fmt.Printf("%v \n", err)
		return nil
	}
	// 总分页数
	pageCount := math.Ceil(float64(total) / float64(pageSize))
	fmt.Printf("getArticlePage pageCount:%d\n", int64(pageCount))
	if page > int64(pageCount) {
		page = int64(pageCount)
	}
	// 偏移量
	offset := (page - 1) * pageSize
	fmt.Printf("getArticlePage offset:%d\n", offset)

	strSql = "SELECT id, title, summary, created_by, created_time, click, like_count, comment_count, category_name, cover, category_id, tags, sticky " +
		"FROM article " +
		"WHERE status=1 " + where + order + " LIMIT ?,?"
	err = variable.Db.Select(&articles, strSql, offset, pageSize)
	if err != nil {
		fmt.Printf("%v \n", err)
		return nil
	}

	for _, article := range articles {
		article.CreatedTimeStr = article.CreatedTime.Format("2006-01-02")

		cover := article.Cover
		if len(cover) == 0 {
			categoryInfo := categoryModel.GetCategoryInfo(article.CategoryId)
			article.Cover = categoryInfo.Cover
		}

		article.Cover = variable.ImgHost + article.Cover

		enId, _ := Lib.EnPwdCode([]byte(strconv.FormatInt(article.Id, 10)))
		article.ArticleId = enId
	}

	pageResult = map[string]interface{}{
		"page":       page,
		"pageSize":   pageSize,
		"pageCount":  int64(pageCount),
		"totalCount": total,
		"data":       articles,
	}

	return pageResult
}

// GetArticleList 文章列表
func (a *ArticleModel) GetArticleList() []*ArticleModel {
	sqlStr := "select id, title, summary, created_by, created_time, click, like_count, comment_count, category_name, cover, category_id, tags, sticky " +
		"from article " +
		"where status=? order by IF(sticky=1, 2, 1) DESC, id desc limit 10"
	var articles []*ArticleModel
	err := variable.Db.Select(&articles, sqlStr, 1)
	if err != nil {
		fmt.Printf("%v \n", err)
		return nil
	}
	return articles
}

// GetArticleInfo 文章详情
func (a *ArticleModel) GetArticleInfo(id int64) ArticleModel {
	go func() {
		_ = a.UpdateArticleClick(id)
	}()

	articleInfo, _ := redis.RedisClient().Get(fmt.Sprintf("Article:Info:%d", id)).Result()
	if articleInfo != "" {
		var articleModel ArticleModel
		err := json.Unmarshal([]byte(articleInfo), &articleModel)
		if err != nil {
			fmt.Printf("error: %s\n", err)
		} else {
			return articleModel
		}
	}

	sqlStr := "select id, title, summary, content, created_by, created_time, click, " +
		"like_count, comment_count, category_name, cover, category_id, tags, sticky " +
		"from article where id=? and status=1"
	var article ArticleModel
	err := variable.Db.Get(&article, sqlStr, id)
	if err != nil {
		fmt.Printf("get failed, err:%v\n", err)
		return ArticleModel{}
	}

	cover := article.Cover
	if len(cover) == 0 {
		categoryInfo := categoryModel.GetCategoryInfo(article.CategoryId)
		article.Cover = categoryInfo.Cover
	}

	article.Cover = variable.ImgHost + article.Cover

	jsonData, _ := json.Marshal(article)
	_, err = redis.RedisClient().Set(fmt.Sprintf("Article:Info:%d", id), jsonData, time.Second*3600).Result()
	if err != nil {
		fmt.Println(err)
	}
	// fmt.Printf("article insert redis, %s\n", jsonResult)
	// fmt.Printf("id:%d name:%s age:%d\n", u.ID, u.Name, u.Age)
	return article
}

// UpdateArticleClick 更新文章点击数
func (a *ArticleModel) UpdateArticleClick(id int64) error {
	// click+FLOOR( 3 + RAND() * (1 - 3))
	sqlStr := "update article set views=views+1, click=click+1 where id=?"
	_, err := variable.Db.Exec(sqlStr, id)
	if err != nil {
		return err
	}
	return nil
}

// ClickLike 点赞
func (a *ArticleModel) ClickLike(id int64) error {
	sqlStr := "update article set like_count=like_count+1 where id=?"
	_, err := variable.Db.Exec(sqlStr, id)
	if err != nil {
		return err
	}
	return nil
}

func (a *ArticleModel) GetArticleCategoryTotal() []map[string]int64 {
	strSql := "SELECT category_id, COUNT(*) AS total FROM article GROUP BY category_id"
	var articleCategoryTotal []struct {
		CategoryId int64 `db:"category_id"`
		Total      int64
	}
	err := variable.Db.Select(&articleCategoryTotal, strSql)
	if err != nil {
		return nil
	}
	var articleCategoryMap []map[string]int64
	for _, item := range articleCategoryTotal {
		mp := map[string]int64{
			"categoryId": item.CategoryId,
			"total":      item.Total,
		}
		articleCategoryMap = append(articleCategoryMap, mp)
	}
	return articleCategoryMap
}

// GetLikeCount 获取点赞总量
func (a *ArticleModel) GetLikeCount() int64 {
	strSql := "SELECT SUM(like_count) AS like_count FROM article"
	var likeCount int64
	err := variable.Db.Get(&likeCount, strSql)
	if err != nil {
		likeCount = 0
	}
	return likeCount
}

func (a *ArticleModel) GetClickCountById(id int64) int64 {
	strSql := "SELECT click AS click_count FROM article WHERE id=?"
	var clickCount int64
	err := variable.Db.Get(&clickCount, strSql, id)
	if err != nil {
		clickCount = 0
		fmt.Println(err)
	}
	return clickCount
}

func (a *ArticleModel) GetLikeCountById(id int64) int64 {
	strSql := "SELECT like_count FROM article WHERE id=?"
	var likeCount int64
	err := variable.Db.Get(&likeCount, strSql, id)
	if err != nil {
		likeCount = 0
		fmt.Println(err)
	}
	return likeCount
}
